import { isServer } from '../is'

// 判断是否为服务器端
const ieVersion = isServer ? 0 : Number((document as any).documentMode)
// 定义特殊字符的正则表达式
const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g
// 定义火狐浏览器的hack正则表达式
const MOZ_HACK_REGEXP = /^moz([A-Z])/

// 导出一个接口，用于表示视口偏移结果
export interface ViewportOffsetResult {
  // 左偏移量
  left: number
  // 上偏移量
  top: number
  // 右偏移量
  right: number
  // 下偏移量
  bottom: number
  // 包含body的右偏移量
  rightIncludeBody: number
  // 包含body的下偏移量
  bottomIncludeBody: number
}

// 定义一个trim函数，用于去除字符串两端的空格
const trim = function (str: string) {
  // 使用正则表达式去除字符串两端的空格
  return (str || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '')
}

/**
 * 将字符串转换为驼峰命名法
 * @param name 字符串
 * @returns
 */
const camelCase = function (name: string) {
  // 使用正则表达式将特殊字符替换为大写字母
  return (
    name
      .replace(SPECIAL_CHARS_REGEXP, function (_, __, letter, offset) {
        // 如果不是第一个字符，则将字母转换为大写，否则保持不变
        return offset ? letter.toUpperCase() : letter
      })
      // 将moz替换为Moz
      .replace(MOZ_HACK_REGEXP, 'Moz$1')
  )
}

/**
 * 判断元素是否包含指定的类名
 * @param el 元素
 * @param className 类名
 * @returns boolean
 */
export function hasClass(el: HTMLElement, className: string) {
  // 如果元素或类名为空，则返回false
  if (!el || !className) return false
  // 如果类名中包含空格，则抛出错误
  if (className.indexOf(' ') !== -1) {
    throw new Error('className should not contain space.')
  }
  // 如果元素支持classList属性，则使用classList.contains方法判断是否包含指定的类名
  if (el.classList) {
    return el.classList.contains(className)
    // 否则，使用indexOf方法判断类名是否在元素的className属性中
  } else {
    return (' ' + el.className + '  ').indexOf(' ' + className + ' ') > -1
  }
}

/**
 * 用于给元素添加类名
 * @param el 元素
 * @param cls 类名
 * @returns
 */
export function addClass(el: HTMLElement, cls: string) {
  // 如果元素不存在，则返回
  if (!el) return
  // 获取元素的当前类名
  let curClass = el.className
  // 将传入的类名字符串按空格分割成数组
  const classes = (cls || '').split(' ')

  // 遍历类名数组
  for (let i = 0, j = classes.length; i < j; i++) {
    // 获取当前类名
    const clsName = classes[i]
    // 如果当前类名为空，则跳过
    if (!clsName) continue

    // 如果元素支持classList属性，则直接添加类名
    if (el.classList) {
      el.classList.add(clsName)
      // 否则，判断元素是否已经包含该类名，如果没有，则将类名添加到当前类名字符串中
    } else if (!hasClass(el, clsName)) {
      curClass += ' ' + clsName
    }
  }
  // 如果元素不支持classList属性，则将当前类名字符串赋值给元素的className属性
  if (!el.classList) {
    el.className = curClass
  }
}

/**
 * 用于给元素移除类名
 * @param el 元素
 * @param cls 类名
 * @returns
 */
export function removeClass(el: HTMLElement, cls: string) {
  if (!el || !cls) return
  // 如果el或cls不存在，则返回
  const classes = cls.split(' ')
  // 将cls字符串按空格分割成数组
  let curClass = ' ' + el.className + ' '

  // 遍历数组
  for (let i = 0, j = classes.length; i < j; i++) {
    const clsName = classes[i]
    // 获取数组中的每一个元素
    if (!clsName) continue

    // 如果el.classList存在，则使用el.classList.remove(clsName)方法移除clsName
    if (el.classList) {
      el.classList.remove(clsName)
      // 否则，使用hasClass(el, clsName)方法判断el是否包含clsName，如果包含，则将curClass中的clsName替换为空格
    } else if (hasClass(el, clsName)) {
      curClass = curClass.replace(' ' + clsName + ' ', ' ')
    }
  }
  // 如果el.classList不存在，则将curClass中的空格替换为空字符串
  if (!el.classList) {
    el.className = trim(curClass)
  }
}

/**
 * 获取元素的位置信息
 * @param element 元素
 * @returns
 */
export function getBoundingClientRect(element: Element): DOMRect | number {
  // 如果元素不存在或者元素没有getBoundingClientRect方法，则返回0
  if (!element || !element.getBoundingClientRect) {
    return 0
  }
  // 否则，返回元素的位置信息
  return element.getBoundingClientRect()
}

/**
 * 获取元素样式
 */
export const getStyle =
  ieVersion < 9
    ? function (element: Element | any, styleName: string) {
        // 如果是服务器端渲染，则返回
        if (isServer) return
        // 如果element或styleName为空，则返回null
        if (!element || !styleName) return null
        // 将styleName转换为驼峰命名
        styleName = camelCase(styleName)
        // 如果styleName为float，则转换为styleFloat
        if (styleName === 'float') {
          styleName = 'styleFloat'
        }
        try {
          // 根据styleName返回对应的样式值
          switch (styleName) {
            case 'opacity':
              try {
                // 如果是IE浏览器，则返回alpha滤镜的opacity值
                return element.filters.item('alpha').opacity / 100
              } catch (e) {
                // 如果没有alpha滤镜，则返回1.0
                return 1.0
              }
            default:
              // 如果是其他样式，则返回element的style或currentStyle中的值
              return element.style[styleName] || element.currentStyle
                ? element.currentStyle[styleName]
                : null
          }
        } catch (e) {
          // 如果发生错误，则返回element的style中的值
          return element.style[styleName]
        }
      }
    : function (element: Element | any, styleName: string) {
        // 如果是服务器端渲染，则返回
        if (isServer) return
        // 如果element或styleName为空，则返回null
        if (!element || !styleName) return null
        // 将styleName转换为驼峰命名
        styleName = camelCase(styleName)
        // 如果styleName为float，则转换为cssFloat
        if (styleName === 'float') {
          styleName = 'cssFloat'
        }
        try {
          // 获取element的computed样式
          const computed = (document as any).defaultView.getComputedStyle(
            element,
            ''
          )
          // 返回element的style或computed中的值
          return element.style[styleName] || computed
            ? computed[styleName]
            : null
        } catch (e) {
          // 如果发生错误，则返回element的style中的值
          return element.style[styleName]
        }
      }

/**
 * 设置元素样式
 * @param element 元素
 * @param styleName 样式名
 * @param value 样式值
 * @returns
 */
export function setStyle(element: Element | any, styleName: any, value: any) {
  if (!element || !styleName) return

  if (typeof styleName === 'object') {
    for (const prop in styleName) {
      if (Object.prototype.hasOwnProperty.call(styleName, prop)) {
        setStyle(element, prop, styleName[prop])
      }
    }
  } else {
    styleName = camelCase(styleName)
    if (styleName === 'opacity' && ieVersion < 9) {
      element.style.filter = isNaN(value)
        ? ''
        : 'alpha(opacity=' + value * 100 + ')'
    } else {
      element.style[styleName] = value
    }
  }
}

/**
 * 判断元素是否可以滚动
 * @param el 元素
 * @param vertical
 * @returns
 */
export const isScroll = (el: Element, vertical: any) => {
  // 如果是在服务器端，则直接返回
  if (isServer) return

  // 判断是否指定了滚动方向
  const determinedDirection = vertical !== null || vertical !== undefined
  // 根据滚动方向获取元素的overflow属性
  const overflow = determinedDirection
    ? vertical
      ? getStyle(el, 'overflow-y')
      : getStyle(el, 'overflow-x')
    : getStyle(el, 'overflow')

  // 判断overflow属性是否匹配scroll或auto，如果匹配则返回true，否则返回false
  return overflow.match(/(scroll|auto)/)
}

/**
 * 获取滚动容器
 * @param el 元素
 * @param vertical
 * @returns
 */
export const getScrollContainer = (el: Element, vertical?: any) => {
  // 如果是在服务器端，则返回
  if (isServer) return

  // 定义一个变量，用于存储父元素
  let parent: any = el
  // 循环遍历父元素
  while (parent) {
    // 如果父元素是window、document或document.documentElement，则返回window
    if ([window, document, document.documentElement].includes(parent)) {
      return window
    }
    // 如果父元素是滚动容器，则返回父元素
    if (isScroll(parent, vertical)) {
      return parent
    }
    // 将父元素设置为父元素的父节点
    parent = parent.parentNode
  }

  // 返回父元素
  return parent
}

/**
 * 判断元素el是否在容器container中
 * @param el 元素
 * @param container 容器
 * @returns
 */
export const isInContainer = (el: Element, container: any) => {
  // 如果在服务器端或者el或container为空，则返回false
  if (isServer || !el || !container) return false

  // 获取元素el的矩形区域
  const elRect = el.getBoundingClientRect()
  let containerRect

  // 如果容器container是window、document、document.documentElement、null或undefined，则设置容器矩形区域为窗口的矩形区域
  if (
    [window, document, document.documentElement, null, undefined].includes(
      container
    )
  ) {
    containerRect = {
      top: 0,
      right: window.innerWidth,
      bottom: window.innerHeight,
      left: 0
    }
  } else {
    // 否则，获取容器container的矩形区域
    containerRect = container.getBoundingClientRect()
  }

  // 判断元素el是否在容器container中
  return (
    elRect.top < containerRect.bottom &&
    elRect.bottom > containerRect.top &&
    elRect.right > containerRect.left &&
    elRect.left < containerRect.right
  )
}

/**
 * ### 移除HTML片段中的标签
 * @param fragment HTML片段字符串；包含HTML标签的字符串和XML标签的字符串
 * @returns HTML片段字符串；不包含HTML标签的字符串
 */
export const removeTag = (fragment: string) => {
  // 创建一个DOM解析器
  const parser = new DOMParser()
  // 解析HTML片段，返回一个Document对象
  return parser.parseFromString(fragment, 'text/html').body.textContent || ''
}
